"""
工具函数模块 - 完整修复版本
提供通用的工具函数
"""

import logging
import sys
import os
import time
import threading
import inspect
from typing import Any, Callable, Optional, Dict, List, Tuple
import tkinter as tk
from tkinter import messagebox
from functools import wraps
from pathlib import Path

# 设置日志
logger = logging.getLogger(__name__)


def setup_logging(level=logging.INFO, log_file: Optional[str] = None,
                  max_bytes: int = 10*1024*1024, backup_count: int = 5) -> bool:
    """设置日志配置 - 完整修复版本"""
    try:
        # 创建日志目录
        if log_file:
            log_dir = os.path.dirname(log_file)
            if log_dir and not os.path.exists(log_dir):
                os.makedirs(log_dir, exist_ok=True)
        else:
            log_file = 'automation_tool.log'

        # 配置日志格式
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
        )

        # 清除现有的处理器
        for handler in logging.root.handlers[:]:
            logging.root.removeHandler(handler)

        # 创建并配置根日志记录器
        logging.root.setLevel(level)

        # 控制台处理器
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.setLevel(level)
        console_handler.setFormatter(formatter)
        logging.root.addHandler(console_handler)

        # 文件处理器（使用RotatingFileHandler防止日志文件过大）
        try:
            from logging.handlers import RotatingFileHandler
            file_handler = RotatingFileHandler(
                log_file,
                maxBytes=max_bytes,
                backupCount=backup_count,
                encoding='utf-8'
            )
            file_handler.setLevel(level)
            file_handler.setFormatter(formatter)
            logging.root.addHandler(file_handler)
        except Exception as e:
            logger.warning(f"创建文件日志处理器失败: {e}")

        # 设置第三方库的日志级别
        logging.getLogger('PIL').setLevel(logging.WARNING)
        logging.getLogger('openpyxl').setLevel(logging.WARNING)
        logging.getLogger('urllib3').setLevel(logging.WARNING)

        logger.info(f"日志系统初始化完成 - 级别: {level}, 文件: {log_file}")
        return True

    except Exception as e:
        print(f"设置日志失败: {e}")
        # 回退到基本配置
        logging.basicConfig(
            level=level,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[logging.StreamHandler(sys.stdout)]
        )
        return False


def safe_execute(func: Callable, *args, default: Any = None,
                error_message: Optional[str] = None, **kwargs) -> Any:
    """安全执行函数，捕获异常 - 完整修复版本"""
    try:
        return func(*args, **kwargs)
    except Exception as e:
        func_name = getattr(func, '__name__', str(func))
        log_message = f"执行函数 {func_name} 时出错: {e}"

        if error_message:
            logger.error(f"{error_message} - {log_message}")
        else:
            logger.error(log_message)

        # 记录堆栈跟踪
        logger.debug(f"异常堆栈:", exc_info=True)

        return default


def retry_on_failure(max_attempts: int = 3, delay: float = 1.0,
                    exceptions: Tuple = (Exception,),
                    logger: Optional[logging.Logger] = None):
    """重试装饰器 - 完整修复版本"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    if logger:
                        logger.warning(f"函数 {func.__name__} 第 {attempt + 1} 次尝试失败: {e}")

                    if attempt < max_attempts - 1:
                        time.sleep(delay * (2 ** attempt))  # 指数退避
                    else:
                        if logger:
                            logger.error(f"函数 {func.__name__} 所有 {max_attempts} 次尝试都失败")
                        raise last_exception
            return None
        return wrapper
    return decorator


def validate_file_path(file_path: str, allowed_extensions: Optional[set] = None,
                      check_exists: bool = True, check_write_access: bool = False) -> Tuple[bool, str]:
    """验证文件路径安全性 - 完整修复版本"""
    try:
        if not file_path or not isinstance(file_path, str):
            return False, "文件路径不能为空"

        # 规范化路径
        try:
            normalized_path = os.path.normpath(file_path)
        except Exception:
            return False, "文件路径格式无效"

        # 防止路径遍历攻击
        if '..' in normalized_path.split(os.sep):
            return False, "文件路径包含非法字符"

        # 检查绝对路径安全性（在Windows上检查驱动器号）
        if os.path.isabs(normalized_path):
            if os.name == 'nt':  # Windows
                drive, path = os.path.splitdrive(normalized_path)
                if not drive:
                    return False, "绝对路径必须包含驱动器号"
            # 允许绝对路径，但可以添加更多安全检查

        # 检查文件扩展名
        if allowed_extensions is not None:
            file_ext = os.path.splitext(normalized_path)[1].lower()
            if file_ext not in allowed_extensions:
                return False, f"不支持的文件扩展名: {file_ext}"

        # 检查文件是否存在
        if check_exists and not os.path.exists(normalized_path):
            return False, f"文件不存在: {normalized_path}"

        # 检查写入权限
        if check_write_access:
            directory = os.path.dirname(normalized_path) or '.'
            if not os.access(directory, os.W_OK):
                return False, f"没有写入权限: {directory}"

        return True, "文件路径有效"

    except Exception as e:
        return False, f"验证文件路径时出错: {e}"


def show_error_dialog(parent: Optional[tk.Widget], message: str,
                     title: str = "错误", detail: Optional[str] = None):
    """显示错误对话框（线程安全） - 完整修复版本"""
    def _show():
        try:
            # 创建详细消息
            full_message = message
            if detail:
                full_message += f"\n\n详细信息:\n{detail}"

            # 限制消息长度，防止对话框过大
            if len(full_message) > 1000:
                full_message = full_message[:1000] + "...\n\n(消息过长，已截断)"

            messagebox.showerror(title, full_message, parent=parent)
        except Exception as e:
            # 如果GUI失败，回退到控制台输出
            print(f"错误对话框显示失败: {e}")
            print(f"错误: {message}")
            if detail:
                print(f"详细信息: {detail}")

    _safe_gui_call(_show, parent)


def show_info_dialog(parent: Optional[tk.Widget], message: str, title: str = "信息"):
    """显示信息对话框（线程安全） - 完整修复版本"""
    def _show():
        try:
            # 限制消息长度
            if len(message) > 1000:
                truncated_message = message[:1000] + "...\n\n(消息过长，已截断)"
                messagebox.showinfo(title, truncated_message, parent=parent)
            else:
                messagebox.showinfo(title, message, parent=parent)
        except Exception as e:
            print(f"信息对话框显示失败: {e}")
            print(f"信息: {message}")

    _safe_gui_call(_show, parent)


def show_warning_dialog(parent: Optional[tk.Widget], message: str, title: str = "警告"):
    """显示警告对话框（线程安全）"""
    def _show():
        try:
            if len(message) > 1000:
                truncated_message = message[:1000] + "...\n\n(消息过长，已截断)"
                messagebox.showwarning(title, truncated_message, parent=parent)
            else:
                messagebox.showwarning(title, message, parent=parent)
        except Exception as e:
            print(f"警告对话框显示失败: {e}")
            print(f"警告: {message}")

    _safe_gui_call(_show, parent)


def show_yesno_dialog(parent: Optional[tk.Widget], message: str,
                     title: str = "确认") -> bool:
    """显示是/否确认对话框（线程安全）"""
    result = [False]  # 使用列表以便在嵌套函数中修改

    def _show():
        try:
            response = messagebox.askyesno(title, message, parent=parent)
            result[0] = response
        except Exception as e:
            print(f"确认对话框显示失败: {e}")
            result[0] = False

    _safe_gui_call(_show, parent)
    return result[0]


def _safe_gui_call(func: Callable, parent: Optional[tk.Widget] = None):
    """安全调用GUI函数（线程安全）"""
    try:
        if parent and hasattr(parent, 'after'):
            # 在主线程中执行
            parent.after(0, func)
        else:
            # 尝试获取根窗口
            root = _get_root_window()
            if root and hasattr(root, 'after'):
                root.after(0, func)
            else:
                # 直接执行
                func()
    except Exception as e:
        logger.error(f"安全GUI调用失败: {e}")
        # 最后尝试直接执行
        try:
            func()
        except:
            pass


def _get_root_window() -> Optional[tk.Tk]:
    """获取根窗口实例"""
    try:
        for widget in tk._default_root.tk.children.values():
            if isinstance(widget, tk.Tk):
                return widget
    except:
        pass
    return None


def resource_path(relative_path: str) -> str:
    """获取资源文件的绝对路径（用于打包后的exe） - 完整修复版本"""
    try:
        # 尝试获取打包后的资源路径
        base_path = getattr(sys, '_MEIPASS', None)
        if base_path:
            path = os.path.join(base_path, relative_path)
            if os.path.exists(path):
                return path

        # 回退到当前文件所在目录
        base_path = os.path.dirname(os.path.abspath(__file__))
        path = os.path.join(base_path, relative_path)

        # 如果还不存在，尝试项目根目录
        if not os.path.exists(path):
            base_path = os.path.dirname(base_path)  # 上一级目录
            path = os.path.join(base_path, relative_path)

        return path

    except Exception as e:
        logger.error(f"获取资源路径失败: {e}")
        return relative_path  # 回退到原始路径


def get_file_size(file_path: str) -> Tuple[bool, int]:
    """获取文件大小（字节）"""
    try:
        if not os.path.exists(file_path):
            return False, 0
        size = os.path.getsize(file_path)
        return True, size
    except Exception as e:
        logger.error(f"获取文件大小失败: {e}")
        return False, 0


def format_file_size(size_bytes: int) -> str:
    """格式化文件大小"""
    if size_bytes == 0:
        return "0 B"

    size_names = ["B", "KB", "MB", "GB", "TB"]
    i = 0
    size = float(size_bytes)

    while size >= 1024 and i < len(size_names) - 1:
        size /= 1024
        i += 1

    return f"{size:.2f} {size_names[i]}"


def is_valid_email(email: str) -> bool:
    """验证电子邮件格式"""
    import re
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))


def sanitize_filename(filename: str) -> str:
    """清理文件名，移除非法字符"""
    import re
    # 移除Windows和Unix/Linux中的非法字符
    illegal_chars = r'[<>:"/\\|?*\x00-\x1f]'
    sanitized = re.sub(illegal_chars, '_', filename)

    # 移除开头和结尾的点号和空格
    sanitized = sanitized.strip('. ')

    # 限制长度
    if len(sanitized) > 255:
        name, ext = os.path.splitext(sanitized)
        sanitized = name[:255 - len(ext)] + ext

    return sanitized if sanitized else "unnamed_file"


def create_backup(file_path: str, backup_suffix: str = ".backup") -> Tuple[bool, str]:
    """创建文件备份"""
    try:
        if not os.path.exists(file_path):
            return False, "原文件不存在"

        backup_path = file_path + backup_suffix

        # 如果备份文件已存在，添加数字后缀
        counter = 1
        while os.path.exists(backup_path):
            backup_path = f"{file_path}{backup_suffix}.{counter}"
            counter += 1

        import shutil
        shutil.copy2(file_path, backup_path)

        logger.info(f"文件备份创建成功: {backup_path}")
        return True, backup_path

    except Exception as e:
        logger.error(f"创建文件备份失败: {e}")
        return False, str(e)


def measure_execution_time(func: Callable) -> Callable:
    """测量函数执行时间的装饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        try:
            result = func(*args, **kwargs)
            return result
        finally:
            end_time = time.time()
            execution_time = end_time - start_time
            logger.debug(f"函数 {func.__name__} 执行时间: {execution_time:.3f}秒")

    return wrapper


class Timer:
    """计时器上下文管理器"""

    def __init__(self, name: str = "操作", logger: Optional[logging.Logger] = None):
        self.name = name
        self.logger = logger or logging.getLogger(__name__)
        self.start_time = None

    def __enter__(self):
        self.start_time = time.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        end_time = time.time()
        execution_time = end_time - self.start_time
        self.logger.info(f"{self.name} 完成，耗时: {execution_time:.3f}秒")


def get_system_info() -> Dict[str, Any]:
    """获取系统信息"""
    import platform
    info = {
        "system": platform.system(),
        "release": platform.release(),
        "version": platform.version(),
        "machine": platform.machine(),
        "processor": platform.processor(),
        "python_version": platform.python_version(),
        "python_implementation": platform.python_implementation(),
    }

    # 添加内存信息（如果可用）
    try:
        import psutil
        memory = psutil.virtual_memory()
        info["memory_total"] = format_file_size(memory.total)
        info["memory_available"] = format_file_size(memory.available)
        info["memory_used_percent"] = f"{memory.percent}%"
    except ImportError:
        info["memory_info"] = "psutil 不可用"

    return info


def check_disk_space(path: str = ".") -> Dict[str, Any]:
    """检查磁盘空间"""
    try:
        import shutil
        total, used, free = shutil.disk_usage(path)

        return {
            "path": os.path.abspath(path),
            "total": format_file_size(total),
            "used": format_file_size(used),
            "free": format_file_size(free),
            "used_percent": f"{(used / total) * 100:.1f}%",
            "free_percent": f"{(free / total) * 100:.1f}%"
        }
    except Exception as e:
        logger.error(f"检查磁盘空间失败: {e}")
        return {"error": str(e)}


def run_in_thread(func: Callable, daemon: bool = True,
                 args: tuple = (), kwargs: Optional[dict] = None) -> threading.Thread:
    """在后台线程中运行函数"""
    if kwargs is None:
        kwargs = {}

    def wrapper():
        try:
            func(*args, **kwargs)
        except Exception as e:
            logger.error(f"后台线程执行失败: {e}")

    thread = threading.Thread(target=wrapper, daemon=daemon)
    thread.start()
    return thread


def is_main_thread() -> bool:
    """检查是否在主线程中"""
    return threading.current_thread() is threading.main_thread()


def synchronized(lock: threading.Lock):
    """同步装饰器（线程安全）"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            with lock:
                return func(*args, **kwargs)
        return wrapper
    return decorator


# 创建全局锁实例
global_lock = threading.Lock()


def memoize(ttl: Optional[float] = None, maxsize: Optional[int] = 128):
    """带TTL的缓存装饰器"""
    def decorator(func):
        cache = {}
        cache_info = {"hits": 0, "misses": 0, "size": 0}

        @wraps(func)
        def wrapper(*args, **kwargs):
            # 创建缓存键
            key = (args, tuple(sorted(kwargs.items())))

            # 检查缓存
            now = time.time()
            if key in cache:
                value, timestamp = cache[key]
                if ttl is None or (now - timestamp) < ttl:
                    cache_info["hits"] += 1
                    return value
                else:
                    # 缓存过期
                    del cache[key]
                    cache_info["size"] -= 1

            # 缓存未命中或过期
            cache_info["misses"] += 1
            value = func(*args, **kwargs)

            # 更新缓存
            cache[key] = (value, now)
            cache_info["size"] += 1

            # 限制缓存大小
            if maxsize and len(cache) > maxsize:
                # 移除最旧的条目
                oldest_key = min(cache.keys(), key=lambda k: cache[k][1])
                del cache[oldest_key]
                cache_info["size"] -= 1

            return value

        # 添加缓存信息方法
        def get_cache_info():
            return cache_info.copy()

        wrapper.get_cache_info = get_cache_info
        wrapper.clear_cache = cache.clear

        return wrapper
    return decorator


def format_timestamp(timestamp: Optional[float] = None,
                    format_str: str = "%Y-%m-%d %H:%M:%S") -> str:
    """格式化时间戳"""
    if timestamp is None:
        timestamp = time.time()
    return time.strftime(format_str, time.localtime(timestamp))


def human_readable_time(seconds: float) -> str:
    """将秒数转换为人类可读的时间格式"""
    if seconds < 1:
        return f"{seconds * 1000:.0f}毫秒"
    elif seconds < 60:
        return f"{seconds:.1f}秒"
    elif seconds < 3600:
        minutes = seconds // 60
        seconds = seconds % 60
        return f"{minutes:.0f}分{seconds:.0f}秒"
    else:
        hours = seconds // 3600
        minutes = (seconds % 3600) // 60
        seconds = seconds % 60
        return f"{hours:.0f}时{minutes:.0f}分{seconds:.0f}秒"